package com.tang.portal.facade.admin.sys;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.tang.common.component.RedisCache;
import com.tang.framework.core.domain.HttpResult;
import com.tang.framework.core.model.LoginUser;
import com.tang.common.model.req.ReqId;
import com.tang.common.constant.Constant;
import com.tang.common.model.req.ReqIds;
import com.tang.common.model.res.ResSysDictValueTableItem;
import com.tang.service.admin.domain.SysDictType;
import com.tang.service.admin.domain.SysDictValue;
import com.tang.service.admin.model.req.ReqSysDictType;
import com.tang.service.admin.model.req.ReqSysDictTypeAdd;
import com.tang.service.admin.model.res.ResSysDictTypeTableItem;
import com.tang.service.admin.model.res.ResSysDictTypeDetail;
import com.tang.service.admin.service.ISysDictTypeService;
import com.tang.service.admin.service.ISysDictValueService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;

import static com.tang.common.utils.excel.ExcelUtils.getStyleStrategy;

/**
 * @author tang jun liang
 * @Date ${gen.dateTime}
 */
@Slf4j
@Service
public class SysDictTypeFacade {

    @Resource
    private ISysDictTypeService sysDictTypeService;

    @Resource
    private ISysDictValueService sysDictValueService;

    @Resource
    private RedisCache redisCache;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ThreadPoolExecutor coolThreadCool;

    @Resource
    private ApplicationEventPublisher publisher;


    @SneakyThrows
    @PostConstruct
    public void init() {
        long start = System.currentTimeMillis();
        List<SysDictType> dictTypes = sysDictTypeService.lambdaQuery()
                .eq(SysDictType::getStatus, true)
                .list();
        CountDownLatch count = new CountDownLatch(dictTypes.size());
        dictTypes.forEach(item -> CompletableFuture.runAsync(() -> {
            try {
                RList<ResSysDictValueTableItem> dictValues = redissonClient.getList(item.getName());
                dictValues.delete();
                List<ResSysDictValueTableItem> dictValueList = sysDictValueService.selectJoinList(
                        ResSysDictValueTableItem.class,
                        new MPJLambdaWrapper<SysDictValue>()
                                .selectAll(SysDictValue.class)
                                .selectAs(SysDictType::getName, ResSysDictValueTableItem::getDictTypeName)
                                .leftJoin(SysDictType.class, SysDictType::getId, SysDictValue::getDictTypeId)
                                .eq(SysDictValue::getDictTypeId, item.getId())
                                .eq(SysDictType::getStatus, true)
                                .leftJoin(SysDictType.class, SysDictType::getId, SysDictValue::getDictTypeId)
                );
                dictValues.addAll(dictValueList);
            } finally {
                count.countDown();
            }
        }, coolThreadCool));
        count.await();
        long end = System.currentTimeMillis();
        log.info("字典缓存成功: 耗时{}毫秒", (end - start));
    }

    @EventListener
    public void reloadCache(ResSysDictValueTableItem sysDictType) {
        init();
    }

    private void reloadCache() {
        reloadCache(null);
    }


    /**
     * 字典类型表删除
     *
     * @param id
     * @param currLoginUser
     * @return
     */
    public HttpResult<String> remove(ReqIds id, LoginUser currLoginUser) {
        sysDictTypeService.removeByIds(id.getIds());
        reloadCache();
        return HttpResult.ok();
    }

    /**
     * 字典类型表详情
     *
     * @param id
     * @param currLoginUser
     * @return
     */
    public HttpResult<ResSysDictTypeDetail> detail(ReqId id, LoginUser currLoginUser) {
        SysDictType sysDictType = sysDictTypeService.getById(id.getId());
        ResSysDictTypeDetail resSysDictTypeDetail = new ResSysDictTypeDetail();
        BeanUtils.copyProperties(sysDictType, resSysDictTypeDetail);
        return HttpResult.ok(resSysDictTypeDetail);
    }

    /**
     * 字典类型表列表
     *
     * @param reqSysDictType
     * @param currLoginUser
     * @return
     */
    public HttpResult<IPage<ResSysDictTypeTableItem>> list(ReqSysDictType reqSysDictType, LoginUser currLoginUser) {
        IPage<ResSysDictTypeTableItem> page = sysDictTypeService.selectJoinListPage(
                new Page<>(reqSysDictType.getPageNum(), reqSysDictType.getPageSize()),
                ResSysDictTypeTableItem.class,
                new MPJLambdaWrapper<SysDictType>()
                        .selectAll(SysDictType.class)
                        .like(StringUtils.isNotBlank(reqSysDictType.getName()), SysDictType::getName, reqSysDictType.getName())
        );
        return HttpResult.ok(page);
    }

    /**
     * 新增字典类型表
     *
     * @param sysDictTypeAdd
     * @return
     */
    public HttpResult<String> add(ReqSysDictTypeAdd sysDictTypeAdd) {
        if (sysDictTypeAdd.getEnum_() && Objects.isNull(sysDictTypeAdd.getId())) {
            Long count = sysDictTypeService
                    .lambdaQuery()
                    .eq(SysDictType::getName, sysDictTypeAdd.getName())
                    .count();
            if (count != 0) {
                return HttpResult.error("已有相同的枚举字典");
            }
        }

        SysDictType sysDictType = new SysDictType();
        BeanUtils.copyProperties(sysDictTypeAdd, sysDictType);
        sysDictTypeService.saveOrUpdate(sysDictType);
        reloadCache();
        return HttpResult.ok();
    }

    /**
     * 更新字典类型表
     *
     * @param sysDictTypeAdd
     * @return
     */
    public HttpResult<String> update(ReqSysDictTypeAdd sysDictTypeAdd) {
        return add(sysDictTypeAdd);
    }


    /**
     * 导出字典类型表表
     *
     * @param ids
     * @param httpServletResponse
     */
    @SneakyThrows
    public void export(ReqIds ids, HttpServletResponse httpServletResponse) {
        String fileName = URLEncoder.encode("字典类型表.xlsx", StandardCharsets.UTF_8);
        // 设置响应体
        httpServletResponse.setContentType("application/vnd.ms-excel");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-disposition", "attachment;filename=" + fileName);
        List<SysDictType> sysDictTypeList = sysDictTypeService
                .lambdaQuery()
                .in(!ids.getIds().isEmpty(), SysDictType::getId, ids.getIds())
                .list();
        EasyExcel.write(httpServletResponse.getOutputStream(), SysDictType.class)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .registerWriteHandler(getStyleStrategy())
                .sheet("字典类型表").doWrite(sysDictTypeList);
    }


    /**
     * 导入excel
     *
     * @param file
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public HttpResult<String> importExcel(MultipartFile file) throws IOException {
        InputStream inputStream = file.getInputStream();
        EasyExcel.read(inputStream, ReqSysDictTypeAdd.class, new AnalysisEventListener<ReqSysDictTypeAdd>() {
            final List<SysDictType> sysDictTypeList = new ArrayList<>();

            @Override
            public void invoke(ReqSysDictTypeAdd sysDictTypeAdd, AnalysisContext analysisContext) {
                SysDictType sysDictType = new SysDictType();
                BeanUtils.copyProperties(sysDictTypeAdd, sysDictType);
                sysDictTypeList.add(sysDictType);
                if (sysDictTypeList.size() == Constant.MAX_BATCH) {
                    sysDictTypeService.saveBatch(sysDictTypeList);
                    sysDictTypeList.clear();
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                sysDictTypeService.saveBatch(sysDictTypeList);
            }
        });
        return HttpResult.ok();
    }

    /**
     * 导出模版
     *
     * @param httpServletResponse
     */
    @SneakyThrows
    public void exportTemplate(HttpServletResponse httpServletResponse) {
        String fileName = URLEncoder.encode("字典类型表导入模版.xlsx", StandardCharsets.UTF_8);
        // 设置响应体
        httpServletResponse.setContentType("application/vnd.ms-excel");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-disposition", "attachment;filename=" + fileName);
        EasyExcel.write(httpServletResponse.getOutputStream(), SysDictType.class)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .registerWriteHandler(getStyleStrategy())
                .sheet("字典类型表").doWrite(List.of());
    }

}
